


#include "serial.h"
#include "system.h"
#include <unistd.h>			//Used for UART
#include <fcntl.h>			//Used for UART
#include <termios.h>		//Used for UART




Serial::Serial(const char *_pszDeviceName, int _iBaud)
    :m_iBaud(0),
     m_hSerialDevice(-1)
{
    init(_pszDeviceName, _iBaud);
}

Serial::~Serial()
{
    if (m_hSerialDevice > 0)
    {
        close(m_hSerialDevice);
    }
}

/* read _iBufSize bytes into _pBuf */
int Serial::read(void *_pBuf, int _iBufSize)
{
    int rx_length = 0;
    if (m_hSerialDevice > 0)
    {
        rx_length = ::read(m_hSerialDevice, _pBuf, _iBufSize);
        if (rx_length < 0)
        {
            rx_length = 0;
        }
    }

    return rx_length;
}

/* reads one line of text (times out if no characters are received in _iTimeoutMs) */
std::string Serial::readln(int _iTimeoutMs)
{
    std::string ret;
    for (int i = 0; i < _iTimeoutMs; i += 10)
    {
        signed char ch;
        while (read(&ch, 1) > 0)
        {
            if ( (ch == '\r') ||
                 (ch == '\n') )
            {
                if (ret.length() > 0)
                {
                    return ret;
                }
            }
            else
            {
                // fix any non-printable characters
                if ( (ch < 0) ||
                     (isprint(ch) == 0) )
                {
                    ch = ' ';
                }

                ret.push_back(ch);
            }

            i = 0;		// reset wait when data was received
        }

        SYSTEM::sleep(10);
    }

 return "";		// returns an empty string if timeout occured, even if characters were read
}

/* writes given buffer */
int Serial::write(const void *_pBuf, int _iSize)
{
    int tx_length = 0;
    if (m_hSerialDevice > 0)
    {
        tx_length = ::write(m_hSerialDevice, _pBuf, _iSize);
        if (tx_length < 0)
        {
            m_strDeviceError = "UART TX error.";
            tx_length = 0;
        }
    }

    return tx_length;
}

/* writes one byte */
int Serial::write(char _ch)
{
    return write(&_ch, 1);
}

/* flush pending data */
void Serial::flush()
{
    for (int i = 0; i < 10; i++)
    {
        signed char ch;
        while (read(&ch, 1) > 0)
        {
            i = 0;		// reset wait when data was received
        }

        SYSTEM::sleep(10);
    }
}

/* opens the serial connection */
void Serial::init(const char *_pszDeviceName, int _iBaud)
{
     m_strDeviceName = _pszDeviceName;
     m_iBaud = _iBaud;

    //-------------------------
     //----- SETUP USART 0 -----
     //-------------------------
     //At bootup, pins 8 and 10 are already set to UART0_TXD, UART0_RXD (ie the alt0 function) respectively

     //OPEN THE UART
     //The flags (defined in fcntl.h):
     //	Access modes (use 1 of these):
     //		O_RDONLY - Open for reading only.
     //		O_RDWR - Open for reading and writing.
     //		O_WRONLY - Open for writing only.
     //
     //	O_NDELAY / O_NONBLOCK (same function) - Enables nonblocking mode. When set read requests on the file can return immediately with a failure status
     //											if there is no input immediately available (instead of blocking). Likewise, write requests can also return
     //											immediately with a failure status if the output can't be written immediately.
     //
     //	O_NOCTTY - When set and path identifies a terminal device, open() shall not cause the terminal device to become the controlling terminal for the process.
     m_hSerialDevice = open(m_strDeviceName.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);		//Open in non blocking read/write mode
     if (m_hSerialDevice <= 0)
     {
         //ERROR - CAN'T OPEN SERIAL PORT
         m_strDeviceError = "Unable to open UART.  Ensure it is not in use by another application.";
     }
     else
     {
         int baud = B0;
         switch (m_iBaud)
         {
            case 9600: baud = B9600; break;
            case 19200: baud = B19200; break;
            case 38400: baud = B38400; break;
            case 57600: baud = B57600; break;
            case 115200: baud = B115200; break;
         }

         //CONFIGURE THE UART
         // The flags (defined in /usr/include/termios.h - see http://pubs.opengroup.org/onlinepubs/007908799/xsh/termios.h.html):
         //	Baud rate:- B1200, B2400, B4800, B9600, B19200, B38400, B57600, B115200, B230400, B460800, B500000, B576000, B921600, B1000000, B1152000, B1500000, B2000000, B2500000, B3000000, B3500000, B4000000
         //	CSIZE:- CS5, CS6, CS7, CS8
         //	CLOCAL - Ignore modem status lines
         //	CREAD - Enable receiver
         //	IGNPAR = Ignore characters with parity errors
         //	ICRNL - Map CR to NL on input (Use for ASCII comms where you want to auto correct end of line characters - don't use for bianry comms!)
         //	PARENB - Parity enable
         //	PARODD - Odd parity (else even)
         struct termios options;
         tcgetattr(m_hSerialDevice, &options);
         options.c_cflag = baud | CS8 | CLOCAL | CREAD;		// Set baud rate
         options.c_iflag = IGNPAR;
         options.c_oflag = 0;
         options.c_lflag = 0;
         tcflush(m_hSerialDevice, TCIFLUSH);

         if ( (baud == B0) ||
              (tcsetattr(m_hSerialDevice, TCSANOW, &options) != 0) )
         {
            m_strDeviceError = "Unable to configure UART. Check baud rate.";
         }
    }
}
